
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;

using Swaf;
using Swaf.BizObj;
using Swaf.BizRule;
using Swaf.Container;
using Swaf.Security;

namespace eCom
{
	public class eComTaxesBizRules : Syrinx.BaseBizRules
	{
		public IBizObjList getTaxDefinitions(IApplication app, IRuleContext context, Swaf.BGuid.Guid companyId)
		{
			FlexiMap m = new FlexiMap();
			m.put("companyId", companyId==null?ActiveCompanyId:companyId);
			IBizObjList taxes = app.appData.get("DA.eCom.GetCompanyTaxDefinitions", m) as IBizObjList;
			return taxes;
		}

		public void updateTaxDefinition(IApplication app, IRuleContext context, IBizObj tax)
		{
			if (tax.isNew)
			{
				tax.put("companyId", ActiveCompanyId);
				app.appData.put("DA.eCom.AddCompanyTaxDefinition", tax);
			}
			else
				app.appData.put("DA.eCom.UpdateCompanyTaxDefinition", tax);
		}

		public void deleteTaxDefinition(IApplication app, IRuleContext context, Swaf.BGuid.Guid taxId)
		{
			FlexiMap m = new FlexiMap();
			m.put("id", taxId);
			app.appData.put("DA.eCom.DeleteCompanyTaxDefinition", m);
		}

		public IBizObjList getProductTaxes(IApplication app, IRuleContext context, Swaf.BGuid.Guid productId)
		{
			FlexiMap m = new FlexiMap();
			m.put("productId", productId);
			IBizObjList taxes = app.appData.get("DA.eCom.GetProductTaxes", m) as IBizObjList;
			return taxes;
		}

		public void setProductTaxes(IApplication app, IRuleContext context, Swaf.BGuid.Guid productId, IBizObjList taxesToRemove, IBizObjList taxesToAdd)
		{
			try
			{
				if (productId != null)
				{
					FlexiMap m = new FlexiMap();
					m.put("productId", productId);
					if (taxesToRemove != null)
						foreach (IBizObj tax in taxesToRemove)
						{
							m.put("taxId", tax.get("id"));
							app.appData.put("DA.eCom.DeleteProductTax", m);
						}
					if (taxesToAdd != null)
						foreach (IBizObj tax in taxesToAdd)
						{
							m.put("taxId", tax.get("id"));
							app.appData.put("DA.eCom.AddProductTax", m);
						}
				}
			}
			catch (Exception e)
			{
				throw new eComException("Error executing setProductTaxes.", e);
			}
		}

		public void updateSalesOrderTaxes(IApplication app, IRuleContext context, object resultValue, object[] inputParams)
		{
			IBizObj salesOrder = inputParams[2] as IBizObj;
			if(salesOrder != null)
				foreach (IBizObj orderItem in (IBizObjList)salesOrder.get("items"))
					updateSalesOrderItemTaxesInt(app, context, salesOrder, orderItem);
		}


		public IBizObj updateSalesOrderItemTaxes(IApplication app, IRuleContext context, object resultValue, object[] inputParams)
		{
			IBizObj orderItem = resultValue as IBizObj;
			if (orderItem != null)
			{
				IResults orc = context.RuleGroup.execute(context, "getCurrentSalesOrderDetails");
				IBizObj salesOrder = orc.CallResults as IBizObj;
				updateSalesOrderItemTaxesInt(app, context, salesOrder, orderItem);
			}
			return orderItem;
		}

		protected void updateSalesOrderItemTaxesInt(IApplication app, IRuleContext context, IBizObj salesOrder, IBizObj orderItem)
		{
			IBizObjList taxes = context.RuleGroup.execute(context, "getProductTaxes", orderItem.get("product.id")).CallResults as IBizObjList;
			if (taxes != null)
			{
				FlexiMap m = new FlexiMap();
				m.put("itemId", orderItem.get("id"));
				m.put("orderId", orderItem.get("orderId"));
				app.appData.put("DA.eCom.DeleteOrderItemTaxes", m);

				foreach (IBizObj tax in taxes)
				{
					decimal taxVal = 0M;
					string bizRuleCalculator = (string)tax.get("bizRuleCalculator");
					if (!Cmn.IsEmpty(bizRuleCalculator))
					{
						IResults rc = context.RuleGroup.execute(context, bizRuleCalculator, salesOrder, orderItem, tax);
						//TODO: If tax calculate failed, we should do something about it.
						taxVal = (decimal)rc.CallResults;
					}
					else
					{
						taxVal = (decimal)tax.get("baseAddOn");
						taxVal += (decimal)orderItem.get("total") * ((decimal)tax.get("percentCost") / 100M);
					}
					m.put("taxId", tax.get("id"));
					m.put("total", taxVal);
					app.appData.put("DA.eCom.AddOrderItemTax", m);
				}
			}
		}

		public IBizObj getSalesOrderTaxes(IApplication app, IRuleContext context, object resultValue, object[] inputParams)
		{
			IBizObj salesOrder = resultValue as IBizObj;
			IBizObjList taxes = app.appData.get("DA.eCom.GetSalesOrderTaxes", salesOrder) as IBizObjList;
			if (taxes != null && taxes.numElements > 0)
			{
				IBizObjList orderTaxes = (IBizObjList)salesOrder.get("taxes");
				Decimal total = 0M;
				Swaf.BGuid.Guid lastTaxId = null;
				for(int p = 0; p < taxes.numElements;p++)
				{
					IBizObj tax = taxes[p];
					total += (Decimal)tax.get("total");
					
					if (!tax.get("taxId").Equals(lastTaxId) || p == taxes.numElements-1)
					{
						IBizObj orderTax = orderTaxes.insertNew();
						orderTax.put("taxId", tax.get("taxId"));
						orderTax.put("name", tax.get("name"));
						orderTax.put("total", total);
						total = 0M;
					}
					lastTaxId = (Swaf.BGuid.Guid)tax.get("taxId");
				}
			}
			return salesOrder;
		}



		public decimal calculateRegionalSaleTax(IApplication app, IRuleContext context, IBizObj salesOrder, IBizObj orderItem, IBizObj taxInfo)
		{
			decimal tax = 0M;

			string salesOrderZip = ((string)salesOrder.get("orderContact.defaultAddress.zipCode")).Trim();
			string orderCity = ((string)salesOrder.get("orderContact.defaultAddress.city")).Trim();
			string orderState = ((string)salesOrder.get("ordercontact.defaultAddress.stateProvince")).Trim();
			string orderCountry = ((string)salesOrder.get("ordercontact.defaultAddress.country.regionCode")).Trim();

			if (!Cmn.IsEmpty(salesOrderZip) && !Cmn.IsEmpty(orderCity) && !Cmn.IsEmpty(orderCountry))
			{
				IResults rc = context.RuleGroup.execute(context, "getRegionalSalesTaxes");
				IBizObjList rtaxes = rc.CallResults as IBizObjList;
				if (rtaxes != null)
				{
					//First check for matching on zip
					foreach (IBizObj rtax in rtaxes)
					{
						string rtaxZip = (string)rtax.get("postalCodeInfo");
						if (!Cmn.IsEmpty(rtaxZip))
						{
							string[] parts = rtaxZip.Split(',');
							foreach (string upart in parts)
							{
								string part = upart.Trim();
								int pos = part.IndexOf('/');
								if (pos == -1)
								{
									if (string.Compare(part, salesOrderZip, true) == 0)
									{
										tax = (decimal)orderItem.get("total") * ((decimal)rtax.get("percentCost") / 100M);
										break;
									}
								}
								else
								{
									string[] rparts = part.Split('/');
									if (rparts.Length == 2 && string.Compare(rparts[0], salesOrderZip, true) <= 0 &&
										string.Compare(rparts[1], salesOrderZip, true) >= 0)
									{
										tax = (decimal)orderItem.get("total") * ((decimal)rtax.get("percentCost") / 100M);
										break;
									}
								}
							}
						}
					}

					//If no match on zip, check for match on city, state, and country
					if (tax == 0M)
						foreach (IBizObj rtax in rtaxes)
							if(string.Compare(orderCity, (string)rtax.get("city"), true) == 0 &&
								string.Compare(orderState, (string)rtax.get("stateProvince"), true) == 0 &&
								string.Compare(orderCountry, (string)rtax.get("countryCode"), true) == 0 &&
								Cmn.IsEmpty(rtax.get("postalCodeInfo")))
							{
								tax = (decimal)orderItem.get("total") * ((decimal)rtax.get("percentCost") / 100M);
								break;
							}
					//If no match on check for match on state, and country
					if (tax == 0M)
						foreach (IBizObj rtax in rtaxes)
							if(string.Compare(orderState, (string)rtax.get("stateProvince"), true) == 0 &&
								string.Compare(orderCountry, (string)rtax.get("countryCode"), true) == 0 &&
								Cmn.IsEmpty(rtax.get("postalCodeInfo")) && Cmn.IsEmpty(rtax.get("city")))
							{
								tax = (decimal)orderItem.get("total") * ((decimal)rtax.get("percentCost") / 100M);
								break;
							}
					//If no match on check for match on country
					if (tax == 0M)
						foreach (IBizObj rtax in rtaxes)
							if(string.Compare(orderCountry, (string)rtax.get("countryCode"), true) == 0 &&
								Cmn.IsEmpty(rtax.get("postalCodeInfo")) && Cmn.IsEmpty(rtax.get("city")) && Cmn.IsEmpty(rtax.get("stateProvince")))
							{
								tax = (decimal)orderItem.get("total") * ((decimal)rtax.get("percentCost") / 100M);
								break;
							}
					
				}
			}

			return tax;		
		}

		public IBizObjList getRegionalSalesTaxes(IApplication app, IRuleContext context)
		{
			FlexiMap m = new FlexiMap();
			m["companyId"] = ActiveCompanyId;
			IBizObjList taxes = app.appData.get("DA.eCom.GetRegionalSalesTaxes", m) as IBizObjList;
			return taxes;
		}

		public void updateRegionalSalesTax(IApplication app, IRuleContext context, IBizObj regionalTax)
		{
			if (regionalTax.isNew)
			{
				regionalTax.put("companyId", ActiveCompanyId);
				int c = (int)app.appData.get("DA.eCom.AddRegionalSalesTax", regionalTax);
			}
			else
			{
				int c = (int)app.appData.get("DA.eCom.UpdateRegionalSalesTax", regionalTax);
			}
		}

		public void deleteRegionalSalesTax(IApplication app, IRuleContext context, Swaf.BGuid.Guid id)
		{
			FlexiMap m = new FlexiMap();
			m["id"] = id;
			int c = (int)app.appData.get("DA.eCom.DeleteRegionalSalesTax", m);
		}
	}
}
